﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using XEvolve.Core.Runtime;
using XEvolve.Core.SPI;

namespace XEvolve.Core
{
    public sealed class Platform
    {
        [ThreadStatic] private static Platform platform;
        static Platform()
        {
            platform = null;
        }

        public static void Initialize(IPlatformProvider platformProvider)
        {
            if (platform != null)
            {
                throw new PlatformException("There is already a platform instance attached to current thread");
            }
            platform = new Platform(platformProvider);
        }

        public static Platform Current
        {
            get
            {
                if (platform == null)
                {
                    throw new PlatformException("There is no platform instance attached to current thread");
                }
                return platform;
            }
        }

        internal readonly IPlatformProvider platformProvider;
        private readonly List<Plugin> plugins;
        private bool isStarted;

        internal Platform(IPlatformProvider platformProvider)
        {
            this.platformProvider = platformProvider;
            plugins = new List<Plugin>();
        }

        public void InstallPlugins(IEnumerable<Plugin> pluginsToBeInstalled)
        {
            if (isStarted) throw new PlatformException("Platform is already started");
            RegisterExtensionPoints(pluginsToBeInstalled);
            plugins.AddRange(pluginsToBeInstalled);
        }

        private void RegisterExtensionPoints(IEnumerable<Plugin> pluginsToBeInstalled)
        {
            foreach (var plugin in pluginsToBeInstalled)
            {
                foreach (var extensionPoint in GetExtensionPoints(plugin))
                {
                    platformProvider.ExtensionRegistry.RegisterExtensionPoint(extensionPoint.Identifier, extensionPoint.IsCachingExtension);
                    extensionPoint.Plugin = plugin.GetType();
                }
            }
        }

        public void Start()
        {
            foreach (var plugin in plugins)
            {
                plugin.Initialize();
            }
            try
            {
                platformProvider.ServiceRegistry.Start();
            }
            catch (Exception e)
            {
                throw new PlatformException("platform can't start", e);
            }
            isStarted = true;
        }

        private static IEnumerable<Plugin.ExtensionPoint> GetExtensionPoints(Plugin plugin)
        {
            var fields = plugin.GetType().GetFields(BindingFlags.Public | BindingFlags.Static);
            return fields.Select(field => field.GetValue(plugin)).Where(value => value is Plugin.ExtensionPoint).Cast<Plugin.ExtensionPoint>();
        }

        public void Stop()
        {
            try
            {
                if (!isStarted) throw new PlatformException("Platform is not started");
                platformProvider.ServiceRegistry.Stop();
            }
            finally
            {
                platform = null;
            }
        }

        public IServiceLocator ServiceLocator
        {
            get
            {
                if (!isStarted) throw new PlatformException("Platform is not started");
                return platformProvider.ServiceLocator;
            }
        }
    }

    public class PlatformException : Exception
    {
        public PlatformException(string message) : base(message)
        {
        }

        public PlatformException(string message, Exception innerException) : base(message, innerException)
        {
        }
    }
}